﻿using System.Collections.Generic;
using System.Linq;
using System.Modules.Constants;
using System.Modules.Contracts.DTO;
using System.Modules.ExceptionHanlding;
using System.Reflection;
using System.Web;
using Microsoft.BusinessData.MetadataModel;
using Microsoft.BusinessData.Runtime;
using Microsoft.Office.Server.UserProfiles;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.BusinessData.MetadataModel;
using Microsoft.SharePoint.BusinessData.SharedService;

namespace System.Modules.SharePoint.Extensions
{
    /// <summary>
    /// SPSite Extensions
    /// </summary>
    public static class SPSiteExtensions
    {
        #region GetSPWeb
        /// <summary>
        /// Use this method to get SP Web.
        /// </summary>
        /// <param name="site"></param>
        /// <param name="webUrl"></param>
        /// <returns></returns>
        public static SPWeb GetSPWeb(this SPSite site, string webUrl)
        {
            return string.IsNullOrEmpty(webUrl) ? site.OpenWeb() : site.OpenWeb(webUrl);
        }
        #endregion

        #region FixLookupField
        /// <summary>
        /// Use this method to restructure Field XML by binding list Id.
        /// </summary>
        /// <param name="site"></param>
        /// <param name="lookUpStaticName"></param>
        /// <param name="listName"></param>
        public static void FixLookupField(this SPSite site, string lookUpStaticName, string listName)
        {
            if (site != null)
            {
                site.RootWeb.FixLookupField(lookUpStaticName, listName);
            }
        }
        #endregion

        #region PopulateChoiceField
        /// <summary>
        /// Use this method to populate choice field from a SP list.
        /// </summary>
        /// <param name="site"></param>
        /// <param name="pickListStaticName"></param>
        /// <param name="listName"></param>
        public static void PopulateChoiceField(this SPSite site, string pickListStaticName, string listName)
        {
            site.RootWeb.PopulateChoiceField(pickListStaticName, listName);
        }

        /// <summary>
        /// Use this method to populate choice field from a list of values.
        /// </summary>
        /// <param name="site"></param>
        /// <param name="pickListStaticName"></param>
        /// <param name="values"></param>
        public static void PopulateChoiceField(this SPSite site, string pickListStaticName, List<string> values)
        {
            site.RootWeb.PopulateChoiceField(pickListStaticName, values);
        }
        #endregion

        #region LoadEnumeratedEmployees
        /// <summary>
        /// Enumerates all User Profile Employees
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="site"></param>
        /// <returns></returns>
        public static List<T> LoadEnumeratedEmployees<T>(this SPSite site) where T : new()
        {
            var value = new List<T>();
            if (site != null)
            {
                var userProfileConfigurationManager = new UserProfileConfigManager(SPServiceContext.GetContext(site));
                ProfilePropertyManager profilePropertyManager = userProfileConfigurationManager.ProfilePropertyManager;
                CorePropertyManager corePropertyManager = profilePropertyManager.GetCoreProperties();
                var userProfileManager = new UserProfileManager(SPServiceContext.GetContext(site));
                foreach (UserProfile userProfile in userProfileManager)
                {
                    value.Add(userProfile.LoadUserProfile<T>(corePropertyManager));
                }
            }
            return value;
        }
        #endregion

        #region Enumerate Employees
        /// <summary>
        /// Enumerates All User Profile Employees (Try Reads from Cache by Default)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="site"></param>
        /// <returns></returns>
        public static List<T> EnumerateEmployees<T>(this SPSite site) where T : new()
        {
            return site.EnumerateEmployees<T>(true);
        }
        #endregion

        #region Enumerate Employees
        /// <summary>
        /// Enumerates All User Profile Employees (Try Reads from Cache by Default)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="site"></param>
        /// <param name="readCache"></param>
        /// <returns></returns>
        public static List<T> EnumerateEmployees<T>(this SPSite site, bool readCache) where T : new()
        {
            string cache = string.Format(CommonConstants.EMPLOYEES_CACHE, site.ID);
            List<T> value;
            if (readCache)
            {
                value = HttpRuntime.Cache[cache] as List<T>;
                if (value == null)
                {
                    value = site.LoadEnumeratedEmployees<T>();
                    HttpRuntime.Cache[cache] = value;
                }
            }
            else
            {
                value = site.LoadEnumeratedEmployees<T>();
            }
            return value;
        }
        #endregion

        #region Cache Employees
        /// <summary>
        /// Cache Enumerated Employees
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="site"></param>
        public static void CacheEmployees<T>(this SPSite site) where T : new()
        {
            HttpRuntime.Cache[string.Format(CommonConstants.EMPLOYEES_CACHE, site.ID)] = site.LoadEnumeratedEmployees<T>();
        }
        #endregion

        #region Load External Content Type Data
        /// <summary>
        /// This method loads external content type data.
        /// </summary>
        /// <param name="spSite">SPSite</param>
        /// <param name="entityName">string</param>
        /// <param name="methodName">string</param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static List<T> LoadExternalContentTypeData<T>(this SPSite spSite, string entityName, string methodName) where T : new()
        {
            var items = new List<T>();
            try
            {
                if (spSite != null)
                {
                    using (var site = new SPSite(spSite.Url))
                    {
                        string nameSpace = site.Url.TrimEnd('/');
                        using (new SPServiceContextScope(SPServiceContext.GetContext(site)))
                        {
                            var service = SPFarm.Local.Services.GetValue<BdcService>(string.Empty);
                            if (service != null)
                            {
                                DatabaseBackedMetadataCatalog catalog = service.GetDatabaseBackedMetadataCatalog(SPServiceContext.Current);
                                if (catalog != null)
                                {
                                    IEntity entity = catalog.TryGetEntity(nameSpace, entityName);
                                    if (entity != null)
                                    {
                                        ILobSystemInstance lobSysteminstance = entity.GetLobSystem().GetLobSystemInstances()[0].Value;
                                        IView view = entity.GetFinderView(methodName);
                                        if (view != null && lobSysteminstance != null)
                                        {
                                            IMethodInstance methodInstance = entity.TryGetMethodInstance(methodName, MethodInstanceType.Finder);
                                            if (view.Fields != null && methodInstance != null)
                                            {
                                                List<IField> fieldCollection = view.Fields.ToList();
                                                using (IEntityInstanceEnumerator iEntityInstanceEnumerator = entity.FindFiltered(methodInstance.GetFilters(), lobSysteminstance))
                                                {
                                                    while (iEntityInstanceEnumerator != null && iEntityInstanceEnumerator.MoveNext())
                                                    {
                                                        var item = new T();
                                                        Type type = item.GetType();
                                                        PropertyInfo[] properties = type.GetProperties();
                                                        foreach (PropertyInfo property in properties)
                                                        {
                                                            IField field = fieldCollection.Find(match => match.Name.Equals(property.Name, StringComparison.InvariantCultureIgnoreCase));
                                                            if (field != null && property.CanWrite)
                                                            {
                                                                if (iEntityInstanceEnumerator.Current != null)
                                                                {
                                                                    object fieldValue = iEntityInstanceEnumerator.Current[field.Name];
                                                                    property.SetValue(item, (fieldValue == null) ? null : Convert.ChangeType(fieldValue, Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType), null);
                                                                }
                                                            }
                                                        }
                                                        items.Add(item);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                exception.Handle();
            }
            return items;
        }
        #endregion

        #region SendEmail
        /// <summary>
        /// Use this method to sends Email from SharePoint.
        /// </summary>
        /// <param name="site"> </param>
        /// <param name="email"></param>
        public static void SendEmail(this SPSite site, EmailActivity email)
        {
            if (site != null)
            {
                site.RootWeb.SendEmail(email);
            }
        }
        #endregion
    }
}